#include "stm32f1xx_hal.h"
#include "flash_on_chip.h"

#include "ota_ram_swap.h"
#include "usart.h"
#define AFIO_MAPR_MEM_MODE (0x3U)
typedef void (*pFunction)(void);
uint32_t app_sp;
uint32_t app_entry;
pFunction JumpToApp;
// 单片机硬重启
void Reboot(void)
{
	__disable_irq();
	__set_FAULTMASK(1); // 关闭总中断
	NVIC_SystemReset(); // 请求单片机重启
	while (1)
	{
		;
	}
}
// stm32f103c8t6空间较小，直接跳转OTA地址运行
void jump_to_OTA(void)
{
    app_sp = *(__IO uint32_t *)OTA_START_ADDR;
    app_entry = *(__IO uint32_t *)(OTA_START_ADDR + 4);
    JumpToApp = (pFunction)app_entry;
    // printf("Jumping to OTA...\r\n");
    // printf("app_sp = 0x%08X, app_entry = 0x%08X\r\n", app_sp, app_entry);

    if ((app_sp & 0x2FFE0000) == 0x20000000)
    {
        // 关闭中断
        __disable_irq();
        for (int i = 0; i < 8; i++)
        {
            NVIC->ICER[i] = 0xFFFFFFFF; // 清除中断使能
            NVIC->ICPR[i] = 0xFFFFFFFF; // 清除中断挂起
        }
        // 关闭外设
        HAL_UART_DeInit(&huart1);
        // HAL_SPI_DeInit(&hspi1);
        HAL_DeInit();

        // 设置向量表到APP起始
        SCB->VTOR = OTA_START_ADDR;
        // 重新设置栈指针
        __set_MSP(app_sp);
        // 跳转执行
        JumpToApp();
    }
    else
    {
        printf("Invalid app stack pointer: 0x%08X\r\n", app_sp);
        for (int i = 0; i < 8; i++)
        {
            printf("0x%08X ", *(__IO uint32_t *)(OTA_START_ADDR + i * 4));
        }
        printf("\n\rNow, Bootloader will reset...\r\n");
        printf("bootloader build time[%s][%s]\r\n", __DATE__, __TIME__);
        Reboot();
    }
}

void jump_to_app(void)
{
    app_sp = *(__IO uint32_t *)APP_START_ADDR;
    app_entry = *(__IO uint32_t *)(APP_START_ADDR + 4);
    JumpToApp = (pFunction)app_entry;

    printf("Jumping to APP...\r\n");
    printf("app_sp = 0x%08X, app_entry = 0x%08X\r\n", app_sp, app_entry);

    if ((app_sp & 0x2FFE0000) == 0x20000000)
    {
        // 关闭中断
        __disable_irq();

        // 关闭外设
        HAL_UART_DeInit(&huart1);
        // HAL_SPI_DeInit(&hspi1);
        HAL_DeInit();

        // 设置向量表到APP起始
        SCB->VTOR = APP_START_ADDR;
        // 重新设置栈指针
        __set_MSP(app_sp);
        // 跳转执行
        JumpToApp();
    }
    else
    {
        printf("Invalid app stack pointer: 0x%08X\r\n", app_sp);
        for (int i = 0; i < 32; i++)
        {
            printf("0x%08X ", *(__IO uint32_t *)(APP_START_ADDR + i * 4));
        }
        printf("\n\rNow, Bootloader will reset...\r\n");
        HAL_Delay(3000);
    }
}
static uint32_t crc32_full_at(uint32_t base, uint32_t size)
{
    uint32_t c = 0xFFFFFFFF;
    for (uint32_t i = 0; i < size; i++)
    {
        c ^= *(uint8_t *)(base + i);
        for (int j = 0; j < 8; j++)
        {
            c = (c & 1) ? (0xEDB88320u ^ (c >> 1)) : (c >> 1);
        }
    }
    return c ^ 0xFFFFFFFF;
}
/**
 * @brief  擦除 OTA 元数据（标志位）
 */
static void meta_clear(void)
{
    HAL_FLASH_Unlock();
    FLASH_EraseInitTypeDef er = {0};
    uint32_t e = 0;
    er.TypeErase = FLASH_TYPEERASE_PAGES;
    er.PageAddress = Flage_PAGE_ADDR;
    er.NbPages = 1;
    HAL_FLASHEx_Erase(&er, &e);
    HAL_FLASH_Lock();
}

/**
 * @brief  从 OTA 暂存区拷贝固件到 APP 区
 * @param  staging_addr 暂存区起始地址（例如 0x0800A000）
 * @param  size         固件长度（字节数）
 * @note   在 Bootloader 中运行，App 不可运行
 */
static void flash_swap_copy(uint32_t staging_addr, uint32_t size)
{
    HAL_FLASH_Unlock();

    uint32_t app_addr = APP_START_ADDR;
    uint32_t app_end = APP_START_ADDR + size;

    /* 边界保护：避免写越界 OTA 区 */
    if (app_end > OTA_START_ADDR)
    {
        HAL_FLASH_Lock();
        return;
    }

    /* 1. 擦除 APP 区  */
    FLASH_EraseInitTypeDef erase;
    uint32_t page_error = 0;

    erase.TypeErase = FLASH_TYPEERASE_PAGES;
    erase.PageAddress = APP_START_ADDR;
    erase.NbPages = (size + FLASH_PAGE_SIZE - 1) / FLASH_PAGE_SIZE;

    if (HAL_FLASHEx_Erase(&erase, &page_error) != HAL_OK)
    {
        HAL_FLASH_Lock();
        return;
    }

    /* 2. 从 OTA 区复制到 APP 区  */
    for (uint32_t addr = 0; addr < size; addr += 2)
    {
        uint16_t halfword;

        uint8_t b1 = *(uint8_t *)(staging_addr + addr);
        uint8_t b2 = (addr + 1 < size) ? *(uint8_t *)(staging_addr + addr + 1) : 0xFF;
        halfword = (uint16_t)(b1 | (b2 << 8));

        if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, app_addr + addr, halfword) != HAL_OK)
        {
            HAL_FLASH_Lock();
            return;
        }
    }

    HAL_FLASH_Lock();
    meta_clear();
}

/**
 * @brief  OTA API：从暂存区拷贝固件到基址（APP 区）
 * @param  staging_addr 暂存区起始地址
 * @param  size         固件大小
 * @note   实际调用的就是 ram_swap_copy()，后者执行完成会复位 MCU。
 */
void OTA_SwapToBase_FromStaging(uint32_t staging_addr, uint32_t size)
{
    flash_swap_copy(staging_addr, size);
}

/**
 * @brief  读取 OTA 元数据（标志位）
 */
static int meta_read(ota_flag_t *m)
{
    memcpy(m, (void *)Flage_PAGE_ADDR, sizeof(*m));
    return (m->magic == 0xB00710AD && m->committed == 0x5AA55AA5) ? 0 : -1;
}

/**
 * @brief  OTA API：启动时检查并执行 OTA 升级（早期调用）
 * @note   该函数应在系统初始化早期调用，以确保在 Flash 被擦写时不会执行代码。
 */
void OTA_CheckAndSwap_Early(void)
{
    ota_flag_t m;
    if (meta_read(&m) != 0)
    {
        // printf("no ota flag!\r\n");
    }
    else if (m.fw_size == 0 || m.fw_size > (OTA_END_ADDR - OTA_START_ADDR - PAGE_SIZE))
    {
        printf("flag data error! \r\n");
        meta_clear();
    }
    else if (crc32_full_at(OTA_START_ADDR, m.fw_size) != m.fw_crc32)
    {
        printf("crc32 error!\r\n");
        meta_clear();
    }
    else
    {
        printf("start copy ota data\r\n");
        OTA_SwapToBase_FromStaging(OTA_START_ADDR, m.fw_size);
    }
    jump_to_OTA();
    // jump_to_app();
}
